﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Collections;
using System.Reflection;
using System.Windows;
using SharePointCommander.Model.PropertySheet;
using SharePointCommander.Binding;

namespace SharePointCommander.Model.TreeView
{
    public class SCTreeViewItem<T> : SCTreeViewItem
    {
        public T SPObject
        {
            get
            {
                return (T)spObject;
            }
            set
            {
                spObject = value;
            }
        }

        public SCTreeViewItem(SCTreeViewItem parentNode, T spObject)
            : base(parentNode, spObject)
        {
            Header = spObject.ToString();
        }

    }

    public abstract class SCTreeViewItem : TreeViewItem
    {
        protected object spObject;
        protected SCTreeViewItem parentNode;
        protected int prevSelectedIndex = -1;

        public SCTreeViewItem ParentNode
        {
            get { return parentNode; }
        }

        public bool HasUpdate
        {
            get
            {
                return spObject.GetType().GetMethod("Update", new Type[0]) != null;
            }
        }

        public SCTreeViewItem(SCTreeViewItem parentNode, object spObject)
        {
            this.parentNode = parentNode;
            this.spObject = spObject;
            ToolTip = spObject.GetType().FullName;

            var methods = (from m in spObject.GetType().GetMethods()
                           where !m.IsConstructor && !m.IsAbstract
                           select new MenuItem()
                           {
                               Header = m.ToString().Replace("_", "__"),
                               Tag = m
                           }).ToList();
            methods.ForEach(m => m.Click += new RoutedEventHandler(method_Click));

            ContextMenu contextMenu = new ContextMenu()
            {
                ItemsSource = methods
            };
            ContextMenu = contextMenu;
        }

        void method_Click(object sender, RoutedEventArgs e)
        {
            MethodInfo method = ((MenuItem)sender).Tag as MethodInfo;
            MessageBox.Show(method.ToString());
        }

        public void Update()
        {
            Type type = spObject.GetType();
            MethodInfo method = type.GetMethod("Update", new Type[0]);
            if (method != null)
            {
                method.Invoke(spObject, null);
            }
        }

        public void Refresh()
        {
            int selectedIndex = Items.OfType<TreeViewItem>().TakeWhile(i => !i.IsSelected).Count();
            prevSelectedIndex = selectedIndex;
            IsExpanded = false;
            ItemsSource = new object[] { null };
            Expanded += new RoutedEventHandler(SCTreeViewItem_Expanded);
            IsExpanded = true;
        }

        void SCTreeViewItem_Expanded(object sender, RoutedEventArgs e)
        {
            if (prevSelectedIndex > -1 && prevSelectedIndex < Items.Count && Items[prevSelectedIndex] != null)
            {
                (Items[prevSelectedIndex] as TreeViewItem).IsSelected = true;
                Expanded -= new RoutedEventHandler(SCTreeViewItem_Expanded);
                prevSelectedIndex = -1;
            }
        }

        public IList<PropertyItem> GetObjectFields()
        {
            return BindingHelper.GetObjectFields(spObject);
        }

        public void SetObjectFields(IList<PropertyItem> objectFields)
        {
            Type type = spObject.GetType();

            foreach (PropertyInfo propertyInfo in type.GetProperties().Where(p => p.CanWrite && filterTypes(p.PropertyType)))
            {
                PropertyItem propertyItem = objectFields.Where(p => p.Name == propertyInfo.Name).First();
                if (propertyItem.ReadWrite && propertyItem.Changed && !(propertyItem is PropertyItemException))
                {
                    propertyInfo.SetValue(spObject, getPropertyItemValue(propertyInfo.PropertyType, propertyItem), null);
                }
            }
            foreach (FieldInfo fieldInfo in type.GetFields().Where(f => filterTypes(f.FieldType)))
            {
                PropertyItem propertyItem = objectFields.Where(p => p.Name == fieldInfo.Name).First();
                if (propertyItem.ReadWrite && propertyItem.Changed && !(propertyItem is PropertyItemException))
                {
                    fieldInfo.SetValue(spObject, getPropertyItemValue(fieldInfo.FieldType, propertyItem));
                }
            }
        }

        private bool filterTypes(Type type)
        {
            return type.Equals(typeof(string)) || type.Equals(typeof(int)) || type.Equals(typeof(bool)) || type.Equals(typeof(DateTime)) ||
                type.IsEnum || type.IsArray;
        }

        private object getPropertyItemValue(Type type, PropertyItem propertyItem)
        {
            if (propertyItem is PropertyItemException)
            {
                return null;
            }
            else
            {
                if (type.Equals(typeof(string)))
                {
                    return ((PropertyItemString)propertyItem).Value;
                }
                else if (type.Equals(typeof(Byte)) || type.Equals(typeof(Int16)) || type.Equals(typeof(Int32)) || type.Equals(typeof(Int64)) 
                    || type.Equals(typeof(UInt16)) || type.Equals(typeof(UInt32)) || type.Equals(typeof(UInt64)))
                {
                    return propertyItem.GetType().GetProperty("Value").GetValue(propertyItem, null);
                }
                else if (type.Equals(typeof(bool)))
                {
                    return ((PropertyItemBoolean)propertyItem).Value;
                }
                else if (type.IsEnum)
                {
                    return ((PropertyItemChoice)propertyItem).Value;
                }
                else if (type.Equals(typeof(DateTime)))
                {
                    return ((PropertyItemDateTime)propertyItem).Value;
                }
                else if (type.IsArray)
                {
                    return ((PropertyItemArray)propertyItem).Value;
                }
                else
                {
                    return ((PropertyItemObject)propertyItem).Value;
                }
            }
        }
    }
}
